use bytes::{buf::UninitSlice, BufMut};

use crate::error::*;

static HEX: [u8; 16] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b',
    b'c', b'd', b'e', b'f',
];

static HEX_UPPER: [u8; 16] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B',
    b'C', b'D', b'E', b'F',
];

#[inline]
fn bin_to_hex_inner(src: &[u8], dst_chunk: &mut UninitSlice) -> usize {
    let dst_ptr = dst_chunk.as_mut_ptr();
    let mut i: usize = 0;

    for v in src {
        let ch1 = (*v >> 4) as usize;
        let ch2 = (*v & 0xf) as usize;
        unsafe {
            *(dst_ptr.add(i)) = HEX[ch1];
        }
        unsafe {
            *(dst_ptr.add(i + 1)) = HEX[ch2];
        }
        i += 2;
    }

    i
}

// 建议: dst预留空间是src的两倍以上.
#[inline]
#[allow(dead_code)]
pub fn bin_to_hex<B>(src: &[u8], dst: &mut B) -> usize
where
    B: BufMut + ?Sized,
{
    let dst_chunk = dst.chunk_mut();
    if dst_chunk.len() >= src.len() * 2 {
        let size = bin_to_hex_inner(src, dst_chunk);
        unsafe {
            dst.advance_mut(size);
        }
        return size;
    }

    let mut size: usize = 0;

    for v in src {
        let ch1 = (*v >> 4) as usize;
        let ch2 = (*v & 0xf) as usize;

        dst.put_slice(&[HEX[ch1], HEX[ch2]]);

        size += 2;
    }

    size
}

#[inline]
fn bin_to_hex_upper_inner(src: &[u8], dst_chunk: &mut UninitSlice) -> usize {
    let dst_ptr = dst_chunk.as_mut_ptr();
    let mut i: usize = 0;

    for v in src {
        let ch1 = (*v >> 4) as usize;
        let ch2 = (*v & 0xf) as usize;
        unsafe {
            *(dst_ptr.add(i)) = HEX_UPPER[ch1];
        }
        unsafe {
            *(dst_ptr.add(i + 1)) = HEX_UPPER[ch2];
        }
        i += 2;
    }

    i
}

// 建议dst预留空间是src的两倍以上.
#[inline]
#[allow(dead_code)]
pub fn bin_to_hex_upper<B>(src: &[u8], dst: &mut B) -> usize
where
    B: BufMut + ?Sized,
{
    let dst_chunk = dst.chunk_mut();
    if dst_chunk.len() >= src.len() * 2 {
        let size = bin_to_hex_upper_inner(src, dst_chunk);
        unsafe {
            dst.advance_mut(size);
        }
        return size;
    }

    let mut size: usize = 0;

    for v in src {
        let ch1 = (*v >> 4) as usize;
        let ch2 = (*v & 0xf) as usize;
        dst.put_slice(&[HEX_UPPER[ch1], HEX_UPPER[ch2]]);

        size += 2;
    }

    size
}

#[inline]
pub fn from_hex_digit(digit: u8) -> Option<u8> {
    match digit {
        b'0'..=b'9' => Some(digit - b'0'),
        b'A'..=b'F' => Some(digit - b'A' + 10),
        b'a'..=b'f' => Some(digit - b'a' + 10),
        _ => None,
    }
}

#[inline]
#[allow(dead_code)]
pub fn hex_to_bin<B>(src: &[u8], dst: &mut B) -> Result<usize>
where
    B: BufMut + ?Sized,
{
    if src.len() % 2 != 0 {
        return Error::e_explain(
            ErrorType::InvalidError,
            "the length of src must be a multiple of 2 in the hex_to_bin",
        );
    }

    let mut size: usize = 0;
    let mut ch: u8 = 0;
    for (idx, v) in src.iter().enumerate() {
        let s = match from_hex_digit(*v) {
            Some(s) => s,
            None => {
                return Error::e_explain(
                    ErrorType::InvalidError,
                    format!("invalid chars {} in the hex_to_bin", *v),
                );
            }
        };

        if idx % 2 == 0 {
            ch = s << 4;
        } else {
            ch |= s;
            dst.put_bytes(ch, 1);
            size += 1;
        }
    }

    Ok(size)
}

#[cfg(test)]
mod tests {
    use crate::utils::encoding::{bin_to_hex, bin_to_hex_upper, hex_to_bin};

    #[test]
    fn test_bin_to_hex() {
        let src = "abcdefghijklm";
        let mut dst = Vec::with_capacity(src.len() * 2);
        let res = bin_to_hex(src.as_bytes(), &mut dst);
        assert_eq!(res, 26);
        assert_eq!(&dst, "6162636465666768696a6b6c6d".as_bytes());
        println!("{} {}", res, String::from_utf8_lossy(&dst));

        let mut dst = Vec::with_capacity(src.len() * 2);
        let res = bin_to_hex_upper(src.as_bytes(), &mut dst);
        assert_eq!(res, 26);
        assert_eq!(&dst, "6162636465666768696A6B6C6D".as_bytes());
        println!("{} {}", res, String::from_utf8_lossy(&dst));

        let src = "6162636465666768696A6B6C6D";
        let mut dst = Vec::with_capacity(src.len());
        let res = hex_to_bin(src.as_bytes(), &mut dst).unwrap();
        assert_eq!(res, 13);
        assert_eq!(&dst, "abcdefghijklm".as_bytes());
        println!("{} {}", res, String::from_utf8_lossy(&dst));

        let src = "abcdefasdasdxzcxzcwdqe123saxzc123!@#$$#%$@@#$";
        let mut dst = Vec::with_capacity(src.len() * 2);
        let res = bin_to_hex(src.as_bytes(), &mut dst);
        assert_eq!(res, 90);
        assert_eq!(&dst, "616263646566617364617364787a63787a63776471653132337361787a63313233214023242423252440402324".as_bytes());
        println!("{} {}", res, String::from_utf8_lossy(&dst));

        let src = "616263646566617364617364787a63787a63776471653132337361787a63313233214023242423252440402324";
        let mut dst = Vec::with_capacity(src.len() * 2);
        let res = hex_to_bin(src.as_bytes(), &mut dst).unwrap();
        assert_eq!(res, 45);
        assert_eq!(
            &dst,
            "abcdefasdasdxzcxzcwdqe123saxzc123!@#$$#%$@@#$".as_bytes()
        );
        println!("{} {}", res, String::from_utf8_lossy(&dst));
    }
}
